{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "#请先运行此代码\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\" \n",
    "#这玩意可以让代码块分行输出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 文件读写"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 读文件\n",
    "* 要以读文件的模式打开一个文件对象，使用Python内置的open()函数，传入文件名和标示符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'hi,my name is MuQingYu,this is my note book'"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = open('课程测试文件\\IOtset.txt', 'r')\n",
    "f.read()\n",
    "f.close()#读完记得关闭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 由于文件读写时都有可能产生IOError，一旦出错，后面的f.close()就不会调用。所以，为了保证无论是否出错都能正确地关闭文件，我们可以使用try ... finally来实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    f = open('/path/to/file', 'r')\n",
    "    print(f.read())\n",
    "finally:\n",
    "    if f:\n",
    "        f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 但是每次都这么写实在太繁琐，所以，Python引入了with语句来自动帮我们调用close()方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('/path/to/file', 'r') as f:\n",
    "    print(f.read())\n",
    "# with表达式其实是try-finally的简写形式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 如果文件很小，read()一次性读取最方便（比如读一个10g的文件就很容易爆内存）；如果不能确定文件大小，反复调用read(size)比较保险；如果是配置文件，调用readlines()最方便："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for line in f.readlines():\n",
    "    print(line.strip()) # 把末尾的'\\n'删掉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### file-like Object\n",
    "* 像open()函数返回的这种有个read()方法的对象，在Python中统称为file-like Object。除了file外，还可以是内存的字节流，网络流，自定义流等等。file-like Object不要求从特定类继承，只要写个read()方法就行。\n",
    "\n",
    "* StringIO就是在内存中创建的file-like Object，常用作临时缓冲。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二进制文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 前面讲的默认都是读取文本文件，并且是UTF-8编码的文本文件。要读取二进制文件，比如图片、视频等等，用'rb'模式打开文件即可："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('/Users/michael/test.jpg', 'rb')\n",
    "f.read()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 字符编码\n",
    "* 要读取非UTF-8编码的文本文件，需要给open()函数传入encoding参数，例如，读取GBK编码的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'浣犲ソ锛屾垜鏄姹夊瓧'"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = open('课程测试文件\\GBKtest.txt', 'r',encoding='gbk',errors='ignore')#这样就可以读取汉字了（GBK全称《汉字内码扩展规范》）\n",
    "#遇到有些编码不规范的文件，你可能会遇到UnicodeDecodeError，因为在文本文件中可能夹杂了一些非法编码的字符。\n",
    "# 遇到这种情况，open()函数还接收一个errors参数，表示如果遇到编码错误后如何处理。最简单的方式是直接忽略,errors='ignore'\n",
    "f.read()\n",
    "f.close()#读完记得关闭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 写文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 写文件和读文件是一样的，唯一区别是调用open()函数时，传入标识符'w'或者'wb'表示写文本文件或写二进制文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = open('课程测试文件\\IOtset.txt', 'w')\n",
    "f.write('Hello, world!')\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 你可以反复调用write()来写入文件，但是务必要调用f.close()来关闭文件。当我们写文件时，操作系统往往不会立刻把数据写入磁盘，而是放到内存缓存起来，空闲的时候再慢慢写入。只有调用close()方法时，操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘，剩下的丢失了。所以，还是用with语句来得保险："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "with open('课程测试文件\\IOtset.txt', 'w') as f:\n",
    "    f.write('Hello, world!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 在文件末尾增加文本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = open('课程测试文件\\IOtset.txt', 'a')\n",
    "f.write('more!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# StringIO和BytesIO"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### StringIO"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* StringIO顾名思义就是在内存中读写str。\n",
    "\n",
    "要把str写入StringIO，我们需要先创建一个StringIO，然后，像文件一样写入即可："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world!\n"
     ]
    }
   ],
   "source": [
    "from io import StringIO\n",
    "f = StringIO()\n",
    "f.write('hello')\n",
    "f.write(' ')\n",
    "f.write('world!')\n",
    "print(f.getvalue())#getvalue()方法用于获得写入后的str"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 要读取StringIO，可以用一个str初始化StringIO，然后，像读文件一样读取："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello!\n",
      "Hi!\n",
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "from io import StringIO\n",
    "f = StringIO('Hello!\\nHi!\\nGoodbye!')\n",
    "while True:\n",
    "     s = f.readline()\n",
    "     if s == '':\n",
    "         break\n",
    "     print(s.strip())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### BytesIO"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* StringIO操作的只能是str，如果要操作二进制数据，就需要使用BytesIO。\n",
    "\n",
    "BytesIO实现了在内存中读写bytes，我们创建一个BytesIO，然后写入一些bytes："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'\n"
     ]
    }
   ],
   "source": [
    "from io import BytesIO\n",
    "f = BytesIO()\n",
    "f.write('中文'.encode('utf-8'))#写入经过UTF-8编码的bytes\n",
    "print(f.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 和StringIO类似，可以用一个bytes初始化BytesIO，然后，像读文件一样读取："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from io import BytesIO\n",
    "f = BytesIO(b'\\xe4\\xb8\\xad\\xe6\\x96\\x87')\n",
    "f.read()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 操作文件和目录"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* os模块的基本功能："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'nt'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "os.name # 操作系统类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 环境变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.environ# 操作系统中定义的环境变量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 要获取某个环境变量的值，可以调用os.environ.get('key')："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'F:\\\\anaconda3;F:\\\\anaconda3\\\\Library\\\\mingw-w64\\\\bin;F:\\\\anaconda3\\\\Library\\\\usr\\\\bin;F:\\\\anaconda3\\\\Library\\\\bin;F:\\\\anaconda3\\\\Scripts;F:\\\\anaconda3\\\\bin;F:\\\\anaconda3\\\\condabin;C:\\\\Windows\\\\system32;C:\\\\Windows;C:\\\\Windows\\\\System32\\\\Wbem;C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0;C:\\\\Windows\\\\System32\\\\OpenSSH;F:\\\\Git\\\\cmd;C:\\\\Program Files\\\\dotnet;F:\\\\Matlab2021a\\\\runtime\\\\win64;F:\\\\Matlab2021a\\\\bin;F:\\\\Matlab2021a\\\\polyspace\\\\bin;C:\\\\Program Files\\\\NVIDIA Corporation\\\\NVIDIA NvDLISR;C:\\\\Program Files (x86)\\\\NVIDIA Corporation\\\\PhysX\\\\Common;C:\\\\Users\\\\89859\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python310\\\\Scripts;C:\\\\Users\\\\89859\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python310;C:\\\\Users\\\\89859\\\\AppData\\\\Local\\\\Microsoft\\\\WindowsApps;D:\\\\Bandizip;F:\\\\Microsoft VS Code\\\\bin;C:\\\\Users\\\\89859\\\\.dotnet\\\\tools'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "os.environ.get('PATH')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 操作文件和目录"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 操作文件和目录的函数一部分放在os模块中，一部分放在os.path模块中，这一点要注意一下。查看、创建和删除目录可以这么调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 查看当前目录的绝对路径:\n",
    "os.path.abspath('.')\n",
    "# 在某个目录下创建一个新目录，首先把新目录的完整路径表示出来:\n",
    "os.path.join('/Users/michael', 'testdir')\n",
    "# 然后创建一个目录:\n",
    "os.mkdir('/Users/michael/testdir')\n",
    "# 删掉一个目录:\n",
    "os.rmdir('/Users/michael/testdir')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 把两个路径合成一个时，不要直接拼字符串，而要通过os.path.join()函数，这样可以正确处理不同操作系统的路径分隔符。在Linux/Unix/Mac下，os.path.join()返回这样的字符串：\n",
    " part-1/part-2\n",
    "* 而Windows下会返回这样的字符串：\n",
    "  part-1\\part-2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 同样的道理，要拆分路径时，也不要直接去拆字符串，而要通过os.path.split()函数，这样可以把一个路径拆分为两部分，后一部分总是最后级别的目录或文件名："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('/Users/michael/testdir', 'file.txt')"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "os.path.split('/Users/michael/testdir/file.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* os.path.splitext()可以直接让你得到文件扩展名，很多时候非常方便："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('/path/to/file', '.txt')"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "os.path.splitext('/path/to/file.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 这些合并、拆分路径的函数并不要求目录和文件要真实存在，它们只对字符串进行操作。\n",
    "\n",
    "* 文件操作使用下面的函数。假定当前目录下有一个test.txt文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.rename('test.txt', 'test.py')# 对文件重命名\n",
    "os.remove('test.py')# 删掉文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 但是复制文件的函数居然在os模块中不存在！原因是复制文件并非由操作系统提供的系统调用。理论上讲，我们通过上一节的读写文件可以完成文件复制，只不过要多写很多代码。\n",
    "\n",
    "* 幸运的是shutil模块提供了copyfile()的函数，你还可以在shutil模块中找到很多实用函数，它们可以看做是os模块的补充。\n",
    "\n",
    "* 最后看看如何利用Python的特性来过滤文件。比如我们要列出当前目录下的所有目录，只需要一行代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['.git', 'NumPy基础知识', '课程测试文件']"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[x for x in os.listdir('.') if os.path.isdir(x)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 要列出所有的.py文件，也只需一行代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 序列化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 我们把变量从内存中变成可存储或传输的过程称之为序列化，在Python中叫pickling，在其他语言中也被称之为serialization，marshalling，flattening等等，都是一个意思。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 反过来，把变量内容从序列化的对象重新读到内存里称之为反序列化，即unpickling。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Python提供了pickle模块来实现序列化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'\\x80\\x04\\x95$\\x00\\x00\\x00\\x00\\x00\\x00\\x00}\\x94(\\x8c\\x04name\\x94\\x8c\\x03Bob\\x94\\x8c\\x03age\\x94K\\x14\\x8c\\x05score\\x94KXu.'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pickle\n",
    "d = dict(name='Bob', age=20, score=88)\n",
    "pickle.dumps(d)# 把一个对象序列化并写入文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* pickle.dumps()方法把任意对象序列化成一个bytes，然后，就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('dump.txt', 'wb')\n",
    "pickle.dump(d, f)\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 看看写入的dump.txt文件，一堆乱七八糟的内容，这些都是Python保存的对象内部信息。\n",
    "\n",
    "* 当我们要把对象从磁盘读到内存时，可以先把内容读到一个bytes，然后用pickle.loads()方法反序列化出对象，也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 'Bob', 'age': 20, 'score': 88}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = open('dump.txt', 'rb')\n",
    "d = pickle.load(f)\n",
    "f.close()\n",
    "d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### JSON"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 如果我们要在不同的编程语言之间传递对象，就必须把对象序列化为标准格式，比如XML，但更好的方法是序列化为JSON，因为JSON表示出来就是一个字符串，可以被所有语言读取，也可以方便地存储到磁盘或者通过网络传输"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| JSON类型   | Python类型 |\n",
    "| ---------- | ---------- |\n",
    "| {}         | dict       |\n",
    "| []         | list       |\n",
    "| \"string\"   | str        |\n",
    "| 1234.56    | int或float |\n",
    "| true/false | True/False |\n",
    "| null       | None       |\n",
    "* Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{\"name\": \"Bob\", \"age\": 20, \"score\": 88}'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import json\n",
    "d = dict(name='Bob', age=20, score=88)\n",
    "json.dumps(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `dumps()`方法返回一个 `str`，内容就是标准的JSON。类似的，`dump()`方法可以直接把JSON写入一个 `file-like Object`。\n",
    "\n",
    "* 要把JSON反序列化为Python对象，用 `loads()`或者对应的 `load()`方法，前者把JSON的字符串反序列化，后者从 `file-like Object`中读取字符串并反序列化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'age': 20, 'score': 88, 'name': 'Bob'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "json_str = '{\"age\": 20, \"score\": 88, \"name\": \"Bob\"}'\n",
    "json.loads(json_str)# 由于JSON标准规定JSON编码是UTF-8，所以我们总是能正确地在Python的str与JSON的字符串之间转换。"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "2e918aaa81d99c652401bdd1a0c185581595fb477ac919641bd65261b5d7782a"
  },
  "kernelspec": {
   "display_name": "Python 3.9.7 64-bit ('base': conda)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
